package io.gitee.thinkbungee.crud.mongo.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.gitee.thinkbungee.constant.SatelliteConstant;
import io.gitee.thinkbungee.crud.mongo.annotation.CreateTime;
import io.gitee.thinkbungee.crud.mongo.annotation.InitValue;
import io.gitee.thinkbungee.crud.mongo.annotation.UpdateTime;
import io.gitee.thinkbungee.crud.mongo.bean.MongoPage;
import io.gitee.thinkbungee.crud.mongo.bean.SortBuilder;
import io.gitee.thinkbungee.crud.mongo.bean.UpdateBuilder;
import io.gitee.thinkbungee.crud.mongo.criteria.CriteriaAndWrapper;
import io.gitee.thinkbungee.crud.mongo.criteria.CriteriaWrapper;
import io.gitee.thinkbungee.crud.mongo.entity.SlowQuery;
import io.gitee.thinkbungee.crud.mongo.reflection.SerializableFunction;
import io.gitee.thinkbungee.crud.mongo.utils.FunctionResolveUtils;
import io.gitee.thinkbungee.utils.SystemUtils;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.*;

/**
 * mongo的增删改查
 *
 * @author chenyichang
 * @date 2022/4/18
 */
@Service
@Slf4j
@ConditionalOnClass({MongoTemplate.class})
public class MongoCRUDHelper {

    @Autowired
    protected MongoTemplate mongoTemplate;

    protected QueryMapper queryMapper;
    protected UpdateMapper updateMapper;
    @Autowired
    private MongoConverter mongoConverter;

    /**
     * 是否打印mongo查询语句，默认false不开启
     */
    @Value("${spring.data.mongodb.showSql:false}")
    protected Boolean showSql;
    /**
     * 是否开启慢查询语句到数据库中，默认false不开启
     */
    @Value("${spring.data.mongodb.slowQuery:false}")
    protected Boolean slowQuery;
    /**
     * 慢查询最短时间，默认1000毫秒
     */
    @Value("${spring.data.mongodb.slowTime:1000}")
    protected Long slowTime;

    @PostConstruct
    public void init() {
        queryMapper = new QueryMapper(mongoConverter);
        updateMapper = new UpdateMapper(mongoConverter);
    }


    //=========insert===========//

    /**
     * 插入或更新
     *
     * @param object 对象
     */
    public String insertOrUpdate(Object object) {
        long time = System.currentTimeMillis();
        String id = (String) ReflectUtil.getFieldValue(object, SatelliteConstant.ID);
        Object objectDB = StrUtil.isNotEmpty(id) ? findById(id, object.getClass()) : null;
        if (objectDB == null) {
            //新增

            //设置创建时间
            setCreateTime(object, time);
            //设置更新时间
            setUpdateTime(object, time);
            //设置默认值
            setInitValue(object);
            //去掉id
            ReflectUtil.setFieldValue(object, SatelliteConstant.ID, null);

            mongoTemplate.save(object);
            //回写id
            id = (String) ReflectUtil.getFieldValue(object, SatelliteConstant.ID);
            //单条插入日志
            logSave(object, time, true);
        } else {
            //更新

            Field[] fields = ReflectUtil.getFields(object.getClass());

            //属性赋值
            for (Field field : fields) {
                //如果是非id属性，并且该属性值不为空，就赋值
                Object fieldValue = ReflectUtil.getFieldValue(object, field);
                if (!field.getName().equals(SatelliteConstant.ID)
                        && fieldValue != null) {
                    //赋值
                    ReflectUtil.setFieldValue(objectDB, field, fieldValue);
                }
            }
            //设置更新时间
            setUpdateTime(objectDB, time);
            mongoTemplate.save(objectDB);
            //更新日志
            logSave(objectDB, time, false);
        }

        return id;
    }

    /**
     * 插入
     *
     * @param object 对象
     * @return 主键id
     */
    public String insert(Object object) {
        ReflectUtil.setFieldValue(object, SatelliteConstant.ID, null);
        return insertOrUpdate(object);
    }

    /**
     * 批量插入
     *
     * @param list 对象列表
     * @param <T>  泛型
     */
    public <T> void insertAll(List<T> list) {
        long time = System.currentTimeMillis();

        list.forEach(object -> {
            //id置空
            ReflectUtil.setFieldValue(object, SatelliteConstant.ID, null);
            //设置创建时间
            setCreateTime(object, time);
            //设置更新时间
            setUpdateTime(object, time);
            //设置默认值
            setInitValue(object);
        });

        mongoTemplate.insertAll(list);
        //批量插入日志
        logSave(list, time);

    }

    //=========update===========//

    /**
     * 根据id更新
     *
     * @param object 对象
     */
    public void updateById(Object object) {
        String id = (String) ReflectUtil.getFieldValue(object, SatelliteConstant.ID);
        //id为空 不更新
        if (StrUtil.isEmpty(id)) {
            return;
        }
        //该主键id不存在 不更新
        if (findById(id, object.getClass()) == null) {
            return;
        }
        insertOrUpdate(object);
    }

    /**
     * 根据id更新全部字段
     *
     * @param object 对象
     */
    public void updateAllColumnById(Object object) {
        String id = (String) ReflectUtil.getFieldValue(object, SatelliteConstant.ID);
        //id为空 不更新
        if (StrUtil.isEmpty(id)) {
            return;
        }
        //该主键id不存在 不更新
        if (findById(id, object.getClass()) == null) {
            return;
        }
        Long time = System.currentTimeMillis();
        setUpdateTime(object, time);
        mongoTemplate.save(object);
        logSave(object, time, false);
    }

    /**
     * 更新查询到的第一项
     *
     * @param criteriaWrapper 查询条件
     * @param updateBuilder   更新语句
     * @param clazz           类
     */
    public void updateFirst(CriteriaWrapper criteriaWrapper, UpdateBuilder updateBuilder, Class<?> clazz) {
        long time = System.currentTimeMillis();
        Query query = new Query(criteriaWrapper.build());

        mongoTemplate.updateFirst(query, updateBuilder.build(), clazz);
        logUpdate(clazz, query, updateBuilder, false, time);
    }

    /**
     * 更新符合条件所有数据
     *
     * @param criteriaWrapper 查询条件
     * @param updateBuilder   更新语句
     * @param clazz           类
     */
    public void updateMulti(CriteriaWrapper criteriaWrapper, UpdateBuilder updateBuilder, Class<?> clazz) {
        long time = System.currentTimeMillis();
        Query query = new Query(criteriaWrapper.build());
        mongoTemplate.updateMulti(query, updateBuilder.build(), clazz);
        logUpdate(clazz, query, updateBuilder, true, time);
    }

    //=========delete===========//

    /**
     * 根据条件删除
     *
     * @param criteriaWrapper 查询条件
     * @param clazz           类
     */
    public void deleteByQuery(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
        long time = System.currentTimeMillis();
        Query query = new Query(criteriaWrapper.build());
        mongoTemplate.remove(query, clazz);
        logDelete(clazz, query, time);
    }

    /**
     * 根据id删除
     *
     * @param id    主键id
     * @param clazz 类
     */
    public void deleteById(String id, Class<?> clazz) {
        if (StrUtil.isEmpty(id)) {
            return;
        }
        deleteByQuery(new CriteriaAndWrapper().eq(SatelliteConstant.Mongo::getId, id), clazz);
    }

    /**
     * 根据ids批量删除
     *
     * @param ids   主键ids
     * @param clazz 类
     */
    public void deleteByIds(List<String> ids, Class<?> clazz) {
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }
        deleteByQuery(new CriteriaAndWrapper().in(SatelliteConstant.Mongo::getId, ids), clazz);
    }


    //=========分页查询===========//

    /**
     * 分页查询
     *
     * @param criteriaWrapper 查询条件
     * @param page            分页条件
     * @param clazz           类
     * @param <T>             泛型
     * @return MongoPage 分页模型
     */
    public <T> MongoPage<T> findPage(CriteriaWrapper criteriaWrapper, MongoPage<?> page, Class<T> clazz) {
        //默认id倒叙
        SortBuilder sortBuilder = new SortBuilder(SatelliteConstant.Mongo::getId, Sort.Direction.DESC);
        return findPage(criteriaWrapper, sortBuilder, page, clazz);
    }

    /**
     * 分页查询
     */
    public <T> MongoPage<T> findPage(CriteriaWrapper criteriaWrapper, SortBuilder sortBuilder, MongoPage<?> page,
                                     Class<T> clazz) {
        MongoPage<T> result = new MongoPage<>();
        result.setCurrent(page.getCurrent());
        result.setSize(page.getSize());

        //是否需要查询总条数
        if (page.getSearchCount()) {
            result.setTotal(findCountByQuery(criteriaWrapper, clazz));
        }

        //查询列表数据
        Query query = new Query(criteriaWrapper.build());
        query.with(sortBuilder.build());
        query.skip((page.getCurrent() - 1) * page.getSize());
        query.limit((int) page.getSize());

        long time = System.currentTimeMillis();
        List<T> list = mongoTemplate.find(query, clazz);
        logQuery(clazz, query, time);

        result.setRecords(list);
        return result;
    }

    /**
     * 分页查询
     */
    public <T> MongoPage<T> findPage(SortBuilder sortBuilder, MongoPage<?> page, Class<T> clazz) {
        return findPage(new CriteriaAndWrapper(), sortBuilder, page, clazz);
    }

    /**
     * 分页查询
     */
    public <T> MongoPage findPage(MongoPage<?> page, Class<T> clazz) {
        return findPage(new CriteriaAndWrapper(), page, clazz);
    }


    /**
     * 查询数量
     *
     * @param criteriaWrapper 查询条件
     * @param clazz           类
     * @return 符合条件的数量
     */
    public Long findCountByQuery(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
        long time = System.currentTimeMillis();
        Long count = null;
        Query query = new Query(criteriaWrapper.build());
        if (query.getQueryObject().isEmpty()) {
            //查询所有
            count = mongoTemplate.getCollection(getCollectionName(clazz))
                    .estimatedDocumentCount();
        } else {
            count = mongoTemplate.count(query, clazz);
        }
        logCount(clazz, query, time);
        return count;
    }
    //=========query/find===========//
    //无排序的默认都是id倒叙

    /**
     * 根据主键id查询
     *
     * @param id    主键id
     * @param clazz 类
     * @param <T>   泛型
     * @return T对象
     */
    public <T> T findById(String id, Class<T> clazz) {
        if (StrUtil.isEmpty(id)) {
            return null;
        }
        long time = System.currentTimeMillis();
        T t = mongoTemplate.findById(id, clazz);
        logQuery(clazz, Query.query(new CriteriaAndWrapper()
                .eq(SatelliteConstant.Mongo::getId, id)
                .build()), time);
        return t;
    }

    /**
     * 根据条件查询一个记录，默认排序是主键id倒叙
     *
     * @param criteriaWrapper 查询条件
     * @param clazz           类
     * @param <T>             泛型
     * @return 符合条件的第一个T对象
     */
    public <T> T findOne(CriteriaWrapper criteriaWrapper, Class<T> clazz) {
        SortBuilder sortBuilder = new SortBuilder(SatelliteConstant.Mongo::getId, Sort.Direction.DESC);
        return findOne(criteriaWrapper, sortBuilder, clazz);
    }

    /**
     * 根据条件查询一个记录
     */
    public <T> T findOne(CriteriaWrapper criteriaWrapper, SortBuilder sortBuilder, Class<T> clazz) {
        Query query = new Query(criteriaWrapper.build());
        query.limit(1);
        query.with(sortBuilder.build());

        long time = System.currentTimeMillis();
        T t = mongoTemplate.findOne(query, clazz);
        logQuery(clazz, query, time);
        return t;
    }

    /**
     * 根据条件查询一个记录
     */
    public <T> T findOne(SortBuilder sortBuilder, Class<T> clazz) {
        return findOne(new CriteriaAndWrapper(), sortBuilder, clazz);
    }

    /**
     * 查询符合条件的数据，默认id倒叙
     *
     * @param criteriaWrapper 查询条件
     * @param clazz           类
     * @param <T>             泛型
     * @return T对象列表
     */
    public <T> List<T> findList(CriteriaWrapper criteriaWrapper, Class<T> clazz) {
        SortBuilder sortBuilder = new SortBuilder(SatelliteConstant.Mongo::getId, Sort.Direction.DESC);
        return findList(criteriaWrapper, sortBuilder, clazz);
    }

    /**
     * 查询符合条件的数据
     */
    public <T> List<T> findList(CriteriaWrapper criteriaWrapper, SortBuilder sortBuilder, Class<T> clazz) {
        Query query = new Query(criteriaWrapper.build());
        query.with(sortBuilder.build());

        long time = System.currentTimeMillis();
        List<T> list = mongoTemplate.find(query, clazz);
        logQuery(clazz, query, time);
        return list;
    }

    /**
     * 根据条件查询某个属性
     *
     * @param criteriaWrapper 查询条件
     * @param clazz           类
     * @param property        属性
     * @param propertyClass   属性类
     * @param <T>
     * @param <R>
     * @param <E>
     * @return
     */
    public <T, R, E> List<E> findProperties(CriteriaWrapper criteriaWrapper, Class<?> clazz,
                                            SerializableFunction<T, R> property, Class<E> propertyClass) {

        Query query = new Query(criteriaWrapper.build());
        query.fields().include(FunctionResolveUtils.resolve(property));

        long time = System.currentTimeMillis();
        List<?> list = mongoTemplate.find(query, clazz);
        logQuery(clazz, query, time);

        //提取属性值 并组成集合
        return extractProperty(list, FunctionResolveUtils.resolve(property), propertyClass);
    }

    /**
     * 根据条件查询某个属性（属性类型为String）
     */
    public <T, R> List<String> findProperties(CriteriaWrapper criteriaWrapper, Class<?> clazz,
                                              SerializableFunction<T, R> property) {
        return findProperties(criteriaWrapper, clazz, property, String.class);
    }

    /**
     * 根据主键ids查询某个属性（属性类型为String）
     */
    public <T, R> List<String> findPropertiesByIds(List<String> ids, Class<?> clazz,
                                                   SerializableFunction<T, R> property) {
        CriteriaWrapper criteriaWrapper = new CriteriaAndWrapper().in(SatelliteConstant.Mongo::getId, ids);
        return findProperties(criteriaWrapper, clazz, property, String.class);
    }

    /**
     * 根据条件查找某个属性（属性为主键id）
     */
    public List<String> findIds(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
        return findProperties(criteriaWrapper, clazz, SatelliteConstant.Mongo::getId);
    }


    /**
     * 根据主键ids查询集合列表
     *
     * @param ids   主键ids
     * @param clazz 类
     * @param <T>   泛型
     */
    public <T> List<T> findByIds(Collection<String> ids, Class<T> clazz) {
        CriteriaWrapper criteriaWrapper = new CriteriaAndWrapper().in(SatelliteConstant.Mongo::getId, ids);
        return findList(criteriaWrapper, clazz);
    }

    /**
     * 根据主键ids查询集合列表
     */
    public <T> List<T> findByIds(Collection<String> ids, SortBuilder sortBuilder, Class<T> clazz) {
        CriteriaWrapper criteriaWrapper = new CriteriaAndWrapper().in(SatelliteConstant.Mongo::getId, ids);
        return findList(criteriaWrapper, sortBuilder, clazz);
    }

    /**
     * 根据主键ids查询集合列表
     */
    public <T> List<T> findByIds(String[] ids, Class<T> clazz) {
        return findByIds(Arrays.asList(ids), clazz);
    }

    /**
     * 根据主键ids查询集合列表
     */
    public <T> List<T> findByIds(String[] ids, SortBuilder sortBuilder, Class<T> clazz) {
        return findByIds(Arrays.asList(ids), sortBuilder, clazz);
    }


    /**
     * 查询全部
     *
     * @param clazz 类
     * @param <T>   泛型
     * @return 列表
     */
    public <T> List<T> findAll(Class<T> clazz) {
        return findList(new CriteriaAndWrapper(), clazz);
    }

    public <T> List<T> findAll(SortBuilder sortBuilder, Class<T> clazz) {
        return findList(new CriteriaAndWrapper(), sortBuilder, clazz);
    }

    /**
     * 查询全部的主键id
     *
     * @param clazz 类
     */
    public List<String> findAllIds(Class<?> clazz) {
        return findIds(new CriteriaAndWrapper(), clazz);
    }

    //=========count===========//

    /**
     * 全部数量
     */
    public Long countAll(Class<?> clazz) {
        return findCountByQuery(new CriteriaAndWrapper(), clazz);
    }

    /**
     * 查询符合条件的数量
     *
     * @param criteriaWrapper 查询条件
     * @param clazz           类
     */
    public Long count(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
        long time = System.currentTimeMillis();

        Long count = null;
        Query query = new Query(criteriaWrapper.build());
        if (query.getQueryObject().isEmpty()) {
            count = mongoTemplate.getCollection(getCollectionName(clazz)).estimatedDocumentCount();
        } else {
            count = mongoTemplate.count(query, clazz);
        }
        logCount(clazz, query, time);
        return count;
    }


    //=========其他常用操作===========//

    /**
     * 提取list中对象某个属性值并返回新的list
     *
     * @param list          集合
     * @param property      提取属性
     * @param propertyClazz 属性类
     * @param <T>           泛型
     */
    public <T> List<T> extractProperty(List<?> list, String property, Class<T> propertyClazz) {
        Set<T> result = new HashSet<>();
        for (Object obj : list) {
            //通过反射获取值
            Object value = ReflectUtil.getFieldValue(obj, property);
            //类型一致就加入结果集
            if (value != null && value.getClass().equals(propertyClazz)) {
                result.add((T) value);
            }
        }
        return new ArrayList<T>(result);
    }

    /**
     * 增加某一个字段的数量，原子操作
     *
     * @param id       主键id
     * @param property 属性
     * @param count    需要增加的数量值
     * @param clazz    类
     * @param <R>      返回值泛型
     * @param <T>      参数泛型
     */
    public <T, R> void addCountById(String id, SerializableFunction<T, R> property,
                                    Number count, Class<?> clazz) {

        UpdateBuilder updateBuilder = new UpdateBuilder().inc(property, count);

        updateFirst(new CriteriaAndWrapper().eq(SatelliteConstant.Mongo::getId, id),
                updateBuilder, clazz);
    }

    /**
     * 设置默认值
     *
     * @param object 值
     */
    private void setInitValue(Object object) {
        Field[] fields = ReflectUtil.getFields(object.getClass());
        for (Field field : fields) {
            //是否有默认值注解
            if (field.isAnnotationPresent(InitValue.class)) {
                InitValue initValue = field.getAnnotation(InitValue.class);
                String value = initValue.value();
                if (ReflectUtil.getFieldValue(object, field) == null) {
                    //根据字段类型来处理值
                    Class<?> type = field.getType();
                    if (type.equals(String.class)) {
                        ReflectUtil.setFieldValue(object, field, value);
                    }

                    if (type.equals(Short.class)) {
                        ReflectUtil.setFieldValue(object, field, Short.parseShort(value));
                    }

                    if (type.equals(Integer.class)) {
                        ReflectUtil.setFieldValue(object, field, Integer.parseInt(value));
                    }

                    if (type.equals(Long.class)) {
                        ReflectUtil.setFieldValue(object, field, Long.parseLong(value));
                    }

                    if (type.equals(Float.class)) {
                        ReflectUtil.setFieldValue(object, field, Float.parseFloat(value));
                    }

                    if (type.equals(Double.class)) {
                        ReflectUtil.setFieldValue(object, field, Double.parseDouble(value));
                    }

                    if (type.equals(Boolean.class)) {
                        ReflectUtil.setFieldValue(object, field, Boolean.parseBoolean(value));
                    }
                }
            }
        }
    }

    /**
     * 设置更新时间
     *
     * @param object 对象
     * @param time   更新时间
     */
    private void setUpdateTime(Object object, Long time) {
        Field[] fields = ReflectUtil.getFields(object.getClass());
        for (Field field : fields) {
            //获取标注了@UpdateTime注解的字段
            if (field.isAnnotationPresent(UpdateTime.class)
                    //设置时间戳字段类型是Long类型
                    && field.getType().equals(Date.class)) {

                ReflectUtil.setFieldValue(object, field, time);
            }
        }
    }

    /**
     * 设置创建时间
     *
     * @param object 对象
     * @param time   创建时间
     */
    private void setCreateTime(Object object, Long time) {
        Field[] fields = ReflectUtil.getFields(object.getClass());
        for (Field field : fields) {
            //获取标注了@UpdateTime注解的字段
            if (field.isAnnotationPresent(CreateTime.class)
                    //设置时间戳字段类型是Long类型
                    && field.getType().equals(Date.class)) {

                ReflectUtil.setFieldValue(object, field, time);
            }
        }
    }

    /**
     * 通过类获取集合名
     * 优先取@Document注解的值，默认为类名第一个字母小写
     */
    private String getCollectionName(Class<?> clazz) {
        org.springframework.data.mongodb.core.mapping.Document document =
                clazz.getAnnotation(org.springframework.data.mongodb.core.mapping.Document.class);
        if (document != null) {
            if (StrUtil.isNotEmpty(document.value())) {
                return document.value();
            }
            if (StrUtil.isNotEmpty(document.collection())) {
                return document.collection();
            }
        }
        return StrUtil.lowerFirst(clazz.getSimpleName());
    }

    //======================插入慢查询日志===============================//

    /**
     * 将慢查询语句写入数据，方便排查问题
     *
     * @param query     查询语句
     * @param queryTime 查询时间
     */
    private void insertSlowQuery(String query, Long queryTime) {
        if (this.slowQuery) {
            SlowQuery slowQuery = new SlowQuery();
            slowQuery.setQuery(query);
            slowQuery.setCreateTime(DateUtil.formatDateTime(new Date()));
            slowQuery.setQueryTime(queryTime);
            slowQuery.setSystem(SystemUtils.getSystem());

            //保存堆栈
            StringBuilder sb = new StringBuilder();
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            for (StackTraceElement element : stackTrace) {
                sb.append(element.getClassName())
                        .append(".")
                        .append(element.getMethodName())
                        .append(":")
                        .append(element.getLineNumber())
                        .append("\n");
            }
            slowQuery.setStack(sb.toString());
            mongoTemplate.insert(slowQuery);
        }
    }


    //======================打印SQL日志============================//

    /**
     * 打印查询语句
     * <p>
     * todo 打印的不是纯json 后期待优化为json 比如 ObjectId(""), ISOTime("")类型，或者连接符
     *
     * @param clazz     当前查询类
     * @param query     查询语句
     * @param startTime 开始时间
     */
    private void logQuery(Class<?> clazz, Query query, Long startTime) {
        MongoPersistentEntity<?> entity = this.mongoConverter.getMappingContext().getPersistentEntity(clazz);
        Document mappedQuery = this.queryMapper.getMappedObject(query.getQueryObject(), entity);


        //组装语句，并打印
        // db.collectionName.find(queryJson)
        // .projection(fieldJson)
        // .sort(sortJson)
        // .limit(limit)
        // .skip(skip)
        String print = "\n db." + getCollectionName(clazz) + ".find("
                + mappedQuery.toJson()
                + ")";
        if (!query.getFieldsObject().isEmpty()) {
            Document mappedField = this.queryMapper.getMappedObject(query.getFieldsObject(), entity);
            print += ".projection("
                    + mappedField.toJson()
                    + ")";
        }
        if (query.isSorted()) {
            Document mappedSort = this.queryMapper.getMappedObject(query.getSortObject(), entity);
            print += ".sort("
                    + mappedSort.toJson()
                    + ")";
        }
        if (query.getLimit() != 0) {
            print += ".limit("
                    + query.getLimit()
                    + ")";
        }
        if (query.getSkip() != 0) {
            print += ".skip("
                    + query.getSkip()
                    + ")";
        }
        print += ";";

        long queryTime = System.currentTimeMillis() - startTime;
        //慢查询记录
        if (queryTime > this.slowTime) {
            insertSlowQuery(print, queryTime);
        }
        //是否打印SQL
        if (this.showSql) {
            log.info(print + "\n执行耗时" + queryTime + "ms");
        }
    }

    /**
     * 打印查询语句
     *
     * @param clazz     当前查询类
     * @param query     查询语句
     * @param startTime 开始时间
     */
    private void logCount(Class<?> clazz, Query query, Long startTime) {
        MongoPersistentEntity<?> entity = this.mongoConverter.getMappingContext().getPersistentEntity(clazz);
        Document mappedQuery = this.queryMapper.getMappedObject(query.getQueryObject(), entity);
        String print = "\ndb." + getCollectionName(clazz)
                + ".find("
                + mappedQuery.toJson() + ")"
                + ".count();";

        long queryTime = System.currentTimeMillis() - startTime;
        //慢查询记录
        if (queryTime > this.slowTime) {
            insertSlowQuery(print, queryTime);
        }
        //是否打印SQL
        if (this.showSql) {
            log.info(print + "\n执行耗时" + queryTime + "ms");
        }
    }

    /**
     * 打印查询语句
     *
     * @param clazz
     * @param query
     * @param startTime
     */
    private void logDelete(Class<?> clazz, Query query, Long startTime) {

        MongoPersistentEntity<?> entity = this.mongoConverter.getMappingContext().getPersistentEntity(clazz);
        Document mappedQuery = this.queryMapper.getMappedObject(query.getQueryObject(), entity);

        String print = "\ndb." + getCollectionName(clazz) + ".remove("
                + mappedQuery.toJson()
                + ");";
        long queryTime = System.currentTimeMillis() - startTime;
        //慢查询记录
        if (queryTime > this.slowTime) {
            insertSlowQuery(print, queryTime);
        }
        //是否打印SQL
        if (this.showSql) {
            log.info(print + "\n执行耗时" + queryTime + "ms");
        }
    }

    /**
     * 打印查询语句
     *
     * @param updateBuilder 更新语句
     * @param multi         是否更新多条
     */
    private void logUpdate(Class<?> clazz, Query query, UpdateBuilder updateBuilder,
                           boolean multi, Long startTime) {

        MongoPersistentEntity<?> entity = this.mongoConverter.getMappingContext().getPersistentEntity(clazz);
        Document mappedQuery = this.queryMapper.getMappedObject(query.getQueryObject(), entity);
        Document mappedUpdate = this.queryMapper.getMappedObject(updateBuilder.build().getUpdateObject(), entity);

        String print = "\ndb." + getCollectionName(clazz) + ".update("
                + mappedQuery.toJson() + ","
                + mappedUpdate.toJson() + ","
                + "{multi:" + multi + "});";

        long queryTime = System.currentTimeMillis() - startTime;
        //慢查询记录
        if (queryTime > this.slowTime) {
            insertSlowQuery(print, queryTime);
        }
        //是否打印SQL
        if (this.showSql) {
            log.info(print + "\n执行耗时" + queryTime + "ms");
        }
    }

    /**
     * 打印查询语句
     */
    private void logSave(Object object, Long startTime, Boolean isInsert) {
        JSONObject jsonObject = JSONUtil.parseObj(object);

        if (isInsert) {
            jsonObject.remove(SatelliteConstant.ID);
        }

        String print = "\ndb." + getCollectionName(object.getClass()) + ".save("
                + JSONUtil.toJsonPrettyStr(jsonObject)
                + ");";

        long queryTime = System.currentTimeMillis() - startTime;
        //慢查询记录
        if (queryTime > this.slowTime) {
            insertSlowQuery(print, queryTime);
        }
        //是否打印SQL
        if (this.showSql) {
            log.info(print + "\n执行耗时" + queryTime + "ms");
        }
    }

    /**
     * 打印查询语句
     */
    private void logSave(List<?> list, Long startTime) {
        List<JSONObject> cloneList = new ArrayList<>();

        list.forEach(item -> {
            JSONObject jsonObject = JSONUtil.parseObj(item);
            jsonObject.remove(SatelliteConstant.ID);
            cloneList.add(jsonObject);
        });

        String print = "db." + getCollectionName(list.get(0).getClass()) + ".save("
                + JSONUtil.toJsonPrettyStr(cloneList)
                + ");";
        long queryTime = System.currentTimeMillis() - startTime;
        //慢查询记录
        if (queryTime > this.slowTime) {
            insertSlowQuery(print, queryTime);
        }
        //是否打印SQL
        if (this.showSql) {
            log.info(print + "\n执行耗时" + queryTime + "ms");
        }
    }


}
